﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.Data.Common;
using System.Diagnostics;
using System.IO;
using System.Net.Http;
using System.Security.Cryptography;
using System.Text;
using System.Text.RegularExpressions;

namespace AChance.Func
{
    public static class FunExtensions
    {
        #region String
        public static bool IsEmpty(this string caller)
        {
            return caller != null && caller.Length == 0;
        }
        public static bool IsNullOrEmpty(this string caller)
        {
            return string.IsNullOrEmpty(caller);
        }
        public static bool IsNullOrWhiteSpace(this string caller)
        {
            return string.IsNullOrWhiteSpace(caller);
        }
        public static bool EqualsIgnoreCase(this string caller, string value)
        {
            return caller.Equals(value, StringComparison.OrdinalIgnoreCase);
        }
        public static bool ContainsIgnoreCase(this string caller, string value)
        {
            return caller.Contains(value, StringComparison.OrdinalIgnoreCase);
        }
        public static string ReplaceIgnoreCase(this string caller, string oldValue, string newValue)
        {
            return caller.Replace(oldValue, newValue, StringComparison.OrdinalIgnoreCase);
        }
        public static string ReplaceFirst(this string caller, string oldValue, string newValue)
        {
            return (new Regex($"({oldValue}){{1}}")).Replace(caller, newValue, 1, 0);
        }
        /// <summary>
        /// 返回此字符串转换为已过滤掉网络内容中不合法字符的副本 
        /// </summary>
        public static string ToWebContent(this string caller)
        {
            string result = caller;
            string[] filters = new string[] { "<", ">", "?", "|", "'", "#", "\r", "\n" };
            foreach (string filter in filters)
                result = result.Replace(filter, "");
            return result;
        }
        /// <summary>
        /// 返回此字符串转换为已过滤掉本地文件名称中不合法字符的副本
        /// </summary>
        public static string ToSysFileName(this string caller)
        {
            string result = caller;
            string[] filters = new string[] { "<", ">", "?", "|", "\"", "\\", "/", ":", "*" };
            foreach (string filter in filters)
                result = result.Replace(filter, "");
            return result;
        }
        /// <summary>
        /// 返回此字符串转换为已过滤掉网络文件名称中不合法字符的副本
        /// </summary>
        public static string ToWebFileName(this string caller)
        {
            return caller.ToSysFileName().ToWebContent();
        }
        /// <summary>
        /// 返回此字符串转换为已过滤掉Excel工作表名称中不合法字符的副本
        /// </summary>
        public static string ToExcelSheetName(this string caller)
        {
            string result = caller;
            string[] filters = new string[] { "[", "]", "?", "\\", "/", ":", "*" };
            foreach (string filter in filters)
                result = result.Replace(filter, "");
            return result;
        }
        /// <summary>
        /// 判断字符串是否只有数字
        /// </summary>
        public static bool IsNum(this string caller)
        {
            return Regex.IsMatch(caller, "^[0-9]*$");
        }
        /// <summary>
        /// 判断字符串是否只有英文
        /// </summary>
        public static bool IsEn(this string caller)
        {
            return Regex.IsMatch(caller, "^[A-Za-z]*$");
        }
        /// <summary>
        /// 判断字符串是否只有英文大写
        /// </summary>
        public static bool IsEnUpper(this string caller)
        {
            return Regex.IsMatch(caller, "^[A-Z]*$");
        }
        /// <summary>
        /// 判断字符串是否只有英文小写
        /// </summary>
        public static bool IsEnLower(this string caller)
        {
            return Regex.IsMatch(caller, "^[a-z]*$");
        }
        /// <summary>
        /// 判断字符串是否只有汉字
        /// </summary>
        public static bool IsCn(this string caller)
        {
            return Regex.IsMatch(caller, "^[\u4e00-\u9fa5]*$");
        }
        /// <summary>
        /// 判断字符串是否只有数字或英文
        /// </summary>
        public static bool IsNumOrEn(this string caller)
        {
            return Regex.IsMatch(caller, "^[A-Za-z0-9]*$");
        }
        /// <summary>
        /// 判断字符串是否为整数
        /// </summary>
        public static bool IsInteger(this string caller)
        {
            return Regex.IsMatch(caller, "^-?\\d+$");
        }
        /// <summary>
        /// 判断字符串是否为正整数(含0)
        /// </summary>
        public static bool IsIntegerPositive(this string caller)
        {
            return Regex.IsMatch(caller, "^\\d+$");
        }
        /// <summary>
        /// 判断字符串是否为负整数(含0)
        /// </summary>
        public static bool IsIntegerNegative(this string caller)
        {
            return Regex.IsMatch(caller, "^((-\\d+)|(0+))$");
        }
        /// <summary>
        /// 判断字符串是否为浮点数
        /// </summary>
        public static bool IsDouble(this string caller)
        {
            return Regex.IsMatch(caller, "^(-?\\d+)(\\.\\d+)?$");
        }
        /// <summary>
        /// 判断字符串是否为正浮点数(含0)
        /// </summary>
        public static bool IsDoublePositive(this string caller)
        {
            return Regex.IsMatch(caller, "^\\d+(\\.\\d+)?$");
        }
        /// <summary>
        /// 判断字符串是否为负浮点数(含0)
        /// </summary>
        public static bool IsDoubleNegative(this string caller)
        {
            return Regex.IsMatch(caller, "^((-\\d+(\\.\\d+)?)|(0+(\\.0+)?))$");
        }
        /// <summary>
        /// 判断字符串是否为存储日期(19990101)
        /// </summary>
        public static bool IsSqlDate(this string caller)
        {
            return Regex.IsMatch(caller, @"^((((19|20)\d{2})(00?(1|[3-9])|1[012])(00?[1-9]|[12]\d|30))|(((19|20)\d{2})(00?[13578]|1[02])31)|(((19|20)\d{2})00?2(00?[1-9]|1\d|2[0-8]))|((((19|20)([13579][26]|[2468][048]|0[48]))|(2000))00?229))$");
        }
        /// <summary>
        /// 判断字符串是否为存储时间(120101)
        /// </summary>
        public static bool IsSqlTime(this string caller)
        {
            return Regex.IsMatch(caller, "^(20|21|22|23|[0-1][0-9])([0-5][0-9])([0-5][0-9])$");
        }
        /// <summary>
        /// 判断字符串是否为Email地址
        /// </summary>
        public static bool IsEmail(this string caller)
        {
            return Regex.IsMatch(caller, @"([a-zA-Z0-9_\.\-])+\@(([a-zA-Z0-9\-])+\.)+([a-zA-Z0-9]{2,5})+");
        }
        /// <summary>
        /// 判断字符串是否为身份证号
        /// </summary>
        public static bool IsIDCard(this string caller)
        {
            return caller.Length switch
            {
                15 => IsIDCard15(caller),
                18 => IsIDCard18(caller),
                _ => false
            };
        }
        /// <summary>
        /// 判断字符串是否为15位身份证号
        /// </summary>
        public static bool IsIDCard15(this string caller)
        {
            if (caller.Length != 15)
                return false;
            if (!long.TryParse(caller, out long n) || n < Math.Pow(10, 14))
                return false;//数字验证  
            var address = "11x22x35x44x53x12x23x36x45x54x13x31x37x46x61x14x32x41x50x62x15x33x42x51x63x21x34x43x52x64x65x71x81x82x91";
            if (address.IndexOf(caller.Remove(2)) == -1)
                return false;//省份验证  
            var birth = caller.Substring(6, 6).Insert(4, "-").Insert(2, "-");
            if (!DateTime.TryParse(birth, out _))
                return false;//生日验证  
            return true;
        }
        /// <summary>
        /// 判断字符串是否为18位身份证号
        /// </summary>
        public static bool IsIDCard18(this string caller)
        {
            if (caller.Length != 18)
                return false;
            if (!long.TryParse(caller.Remove(17), out long n)
                || n < Math.Pow(10, 16)
                || !long.TryParse(caller.Replace('x', '0').Replace('X', '0'), out _))
                return false;//数字验证  
            var address = "11x22x35x44x53x12x23x36x45x54x13x31x37x46x61x14x32x41x50x62x15x33x42x51x63x21x34x43x52x64x65x71x81x82x91";
            if (address.IndexOf(caller.Remove(2)) == -1)
                return false;//省份验证  
            var birth = caller.Substring(6, 8).Insert(6, "-").Insert(4, "-");
            if (!DateTime.TryParse(birth, out _))
                return false;//生日验证  
            string[] arrVarifyCode = ("1,0,x,9,8,7,6,5,4,3,2").Split(',');
            string[] Wi = ("7,9,10,5,8,4,2,1,6,3,7,9,10,5,8,4,2").Split(',');
            char[] Ai = caller.Remove(17).ToCharArray();
            int sum = 0;
            for (int i = 0; i < 17; i++)
                sum += int.Parse(Wi[i]) * int.Parse(Ai[i].ToString());
            Math.DivRem(sum, 11, out int y);
            if (arrVarifyCode[y] != caller.Substring(17, 1).ToLower())
                return false;//校验码验证  
            return true;//符合GB11643-1999标准  
        }
        public static DateTime ToDate(this string caller)
        {
            return new DateTime(int.Parse(caller.Substring(0, 4)),
                                               int.Parse(caller.Substring(4, 2)),
                                               int.Parse(caller.Substring(6)));
        }
        public static DateTime ToDateTime(this string caller)
        {
            return new DateTime(int.Parse(caller.Substring(0, 4)),
                                               int.Parse(caller.Substring(4, 2)),
                                               int.Parse(caller.Substring(6, 2)),
                                               int.Parse(caller.Substring(8, 2)),
                                               int.Parse(caller.Substring(10, 2)),
                                               int.Parse(caller.Substring(12)));
        }
        public static string ToSqlDate(this string caller)
        {
            return $"{caller.Substring(0, 4)}{caller.Substring(5, 2)}{caller.Substring(8)}".ToDate().ToSqlDate();
        }
        public static string ToDispDate(this string caller, char separator = '-')
        {
            switch (caller.Length)
            {
                case 8:
                    return caller.ToDate().ToDispDate();
                case 6:
                    return $"{caller}01".ToDate().ToDispDate().Substring(0, 7);
                case 4:
                    return caller.IsNum() ? $"{caller.Substring(0, 2)}{separator}{caller.Substring(2)}" : string.Empty;
                default:
                    return string.Empty;
            }
        }
        public static string ToDispCnDate(this string caller)
        {
            switch (caller.Length)
            {
                case 8:
                    return caller.ToDate().ToDispCnDate();
                case 6:
                    return $"{caller}01".ToDate().ToDispCnDate().Substring(0, 8);
                case 4:
                    return caller.IsNum() ? $"{caller.Substring(0, 2)}月{caller.Substring(2)}日" : string.Empty;
                default:
                    return string.Empty;
            }
        }
        public static string ToMD5(this string caller)
        {
            var md5 = MD5.Create().ComputeHash(Encoding.UTF8.GetBytes(caller));
            return BitConverter.ToString(md5).Replace("-", "");
        }
        #endregion

        #region DateTime
        /// <summary>
        /// 获取指定日期所在周的第一天(以星期一为第一天)
        /// </summary>
        public static DateTime GetFirstDayOfWeek(this DateTime caller)
        {
            if (Convert.ToInt32(caller.DayOfWeek) == 0)
                return caller.AddDays(-6);
            else
                return caller.AddDays(1 - Convert.ToInt32(caller.DayOfWeek));
        }
        /// <summary>
        /// 获取指定日期所在周的最后一天(以星期天为最后一天)
        /// </summary>
        public static DateTime GetLastDayOfWeek(this DateTime caller)
        {
            return GetFirstDayOfWeek(caller).AddDays(6);
        }
        public static long ToTimeStamp(this DateTime caller, bool isMillisecond = false)
        {
            var timeSpan = caller - new DateTime(1970, 1, 1, 8, 0, 0, 0);
            return (isMillisecond ? timeSpan.TotalMilliseconds : timeSpan.TotalSeconds).ToLong(false);
        }
        public static string ToSqlDate(this DateTime caller)
        {
            return caller.ToString("yyyyMMdd");
        }
        public static string ToSqlYearMonth(this DateTime caller)
        {
            return caller.ToString("yyyyMM");
        }
        public static string ToSqlYear(this DateTime caller)
        {
            return caller.ToString("yyyy");
        }
        public static string ToSqlTime(this DateTime caller)
        {
            return caller.ToString("HHmmss");
        }
        public static string ToSqlHourMinute(this DateTime caller)
        {
            return caller.ToString("HHmm");
        }
        public static string ToDispDate(this DateTime caller, char separator = '-')
        {
            return caller.ToString(string.Format("yyyy{0}MM{0}dd", separator));
        }
        public static string ToDispYearMonth(this DateTime caller, char separator = '-')
        {
            return caller.ToString(string.Format("yyyy{0}MM", separator));
        }
        public static string ToDispYear(this DateTime caller)
        {
            return caller.ToString("yyyy");
        }
        public static string ToDispTime(this DateTime caller, char separator = ':')
        {
            return caller.ToString(string.Format("HH{0}mm{0}ss", separator));
        }
        public static string ToDispHourMinute(this DateTime caller, char separator = ':')
        {
            return caller.ToString(string.Format("HH{0}mm", separator));
        }
        public static string ToDispCnDate(this DateTime caller)
        {
            return caller.ToString(string.Format("yyyy年MM月dd日"));
        }
        public static string ToDispCnYearMonth(this DateTime caller)
        {
            return caller.ToString(string.Format("yyyy年MM月"));
        }
        public static string ToDispCnYear(this DateTime caller)
        {
            return caller.ToString(string.Format("yyyy年"));
        }
        public static string ToDispCnTime(this DateTime caller)
        {
            return caller.ToString(string.Format("HH点mm分ss秒"));
        }
        public static string ToDispCnHourMinute(this DateTime caller)
        {
            return caller.ToString(string.Format("HH点mm分"));
        }
        #endregion

        #region Int
        public static bool IsZero(this int caller)
        {
            return caller == 0;
        }
        public static bool IsZero(this int? caller)
        {
            return caller != null && ((int)caller).IsZero();
        }
        public static bool IsNullOrZero(this int? caller)
        {
            return caller is null || ((int)caller).IsZero();
        }
        #endregion

        #region Long
        public static bool IsZero(this long caller)
        {
            return caller == 0;
        }
        public static DateTime ToDateTime(this long caller, bool isMillisecond = false)
        {
            var startTime = new DateTime(1970, 1, 1, 8, 0, 0, 0);
            return isMillisecond ? startTime.AddMilliseconds(caller) : startTime.AddSeconds(caller);
        }
        #endregion

        #region Double
        public static bool IsZero(this double caller)
        {
            return Math.Abs(caller) < 0.00005;
        }
        public static bool IsZero(this double? caller)
        {
            return caller != null && ((double)caller).IsZero();
        }
        public static bool IsNullOrZero(this double? caller)
        {
            return caller is null || ((double)caller).IsZero();
        }
        public static bool IsMoreZero(this double caller)
        {
            return caller >= 0.00005;
        }
        public static bool IsMoreZero(this double? caller)
        {
            return ((double)caller).IsMoreZero();
        }
        public static bool IsLessZero(this double caller)
        {
            return caller <= -0.00005;
        }
        public static bool IsLessZero(this double? caller)
        {
            return ((double)caller).IsLessZero();
        }
        public static bool Same(this double caller, double value)
        {
            return Math.Abs(caller - value).IsZero();
        }
        public static bool Same(this double? caller, double value)
        {
            return ((double)caller).Same(value);
        }
        public static bool Same(this double? caller, double? value)
        {
            return ((double)caller).Same((double)value);
        }
        public static double Round(this double caller, int digits = 0, bool round = true, bool floor = true)
        {
            var sign = caller.IsLessZero() ? -1 : 1;
            if (round)
                return sign * Math.Round(Math.Abs(caller), digits, MidpointRounding.AwayFromZero);
            else
            {
                var pow = Math.Pow(10, digits);
                if (floor)
                    return sign * Math.Floor(Math.Abs(caller) * pow) / pow;
                else
                    return sign * Math.Ceiling(Math.Abs(caller) * pow) / pow;
            }
        }
        public static double Round(this double? caller, int digits = 0, bool round = true, bool floor = true)
        {
            return ((double)caller).Round(digits, round, floor);
        }
        public static int ToInt(this double caller, bool round = true, bool floor = true)
        {
            return Convert.ToInt32(caller.Round(0, round, floor));
        }
        public static int ToInt(this double? caller, bool round = true, bool floor = true)
        {
            return ((double)caller).ToInt(round, floor);
        }
        public static long ToLong(this double caller, bool round = true, bool floor = true)
        {
            return Convert.ToInt64(caller.Round(0, round, floor));
        }
        public static long ToLong(this double? caller, bool round = true, bool floor = true)
        {
            return ((double)caller).ToLong(round, floor);
        }
        #endregion

        #region List
        public static bool IsNullOrEmpty<T>(this List<T> caller)
        {
            return caller is null || caller.Count == 0;
        }
        #endregion

        #region DataTable
        public static bool IsNullOrEmpty(this DataTable caller)
        {
            return caller is null || caller.Rows.Count == 0;
        }
        #endregion

        #region DbParameterCollection
        public static bool IsNullOrEmpty(this DbParameterCollection caller)
        {
            return caller is null || caller.Count == 0;
        }
        #endregion

        #region IReadOnlyDictionary
        public static bool IsNullOrEmpty<TKey, TValue>(this IReadOnlyDictionary<TKey, TValue> caller)
        {
            return caller is null || caller.Count == 0;
        }
        #endregion

        #region Array
        public static bool IsNullOrEmpty(this Array caller)
        {
            return caller is null || caller.Length == 0;
        }
        #endregion

        #region HttpClient
        public static bool PostString(this HttpClient caller, string content, out string result)
        {
            return caller.PostString("", content, out result);
        }
        public static bool PostString(this HttpClient caller, string reqUri, string content, out string result)
        {
            return caller.Post(reqUri, new StringContent(content, Encoding.UTF8, "application/json"), out result);
        }
        public static bool Post(this HttpClient caller, HttpContent content, out string result)
        {
            return caller.Post("", content, out result);
        }
        public static bool Post(this HttpClient caller, string reqUri, HttpContent content, out string result)
        {
            //开始计时
            Stopwatch stopwatch = Stopwatch.StartNew();
            try
            {
                result = caller.PostAsync(reqUri, content).Result.Content.ReadAsStringAsync().Result;
                return true;
            }
            catch (Exception ex)
            {
                result = "Rest执行失败(" + caller.BaseAddress + reqUri + ")";
                FunLogs.LogErrorForRestExec(caller.BaseAddress + reqUri, ex);
                return false;
            }
            finally
            {
                //结束计时
                stopwatch.Stop();
                FunLogs.LogWarnForRestExec(caller.BaseAddress + reqUri, stopwatch.Elapsed, true);
            }
        }
        #endregion

        #region Enum
        public static string GetTypeName(this Enum caller)
        {
            return caller.GetType().Name;
        }
        public static string GetName(this Enum caller)
        {
            return caller.ToString();
        }
        public static int GetValue(this Enum caller)
        {
            return caller.GetHashCode();
        }
        #endregion

        #region HashTable
        public static bool IsNullOrEmpty(this Hashtable caller)
        {
            return caller is null || caller.Count == 0;
        }
        public static T GetValue<T>(this Hashtable caller, object key, string notFoundMsg = null, T defaultValue = default)
        {
            var value = caller[key];
            if (value is null || value.GetType() != typeof(T))
                return notFoundMsg.IsNullOrEmpty() ? defaultValue : throw new Exception(notFoundMsg);
            return (T)value;
        }
        #endregion

        #region Type
        public static bool IsNullableEnum(this Type caller)
        {
            return Nullable.GetUnderlyingType(caller)?.IsEnum ?? false;
        }
        #endregion

        #region Stream
        public static string ToMD5(this Stream caller)
        {
            var position = caller.Position;
            caller.Position = 0;
            var md5 = MD5.Create().ComputeHash(caller);
            caller.Position = position;
            return BitConverter.ToString(md5).Replace("-", "");
        }
        #endregion


    }
}
